home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / traceModule.js < prev    next >
Text File  |  2010-01-15  |  54KB  |  1,719 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. /**
  4.  * UI control of debug Logging for Firebug internals
  5.  */
  6. FBL.ns(function() { with (FBL) {
  7.  
  8. // ***********************************************************************************
  9. // Shorcuts and Services
  10.  
  11. const Cc = Components.classes;
  12. const Ci = Components.interfaces;
  13.  
  14. const clipboard = CCSV("@mozilla.org/widget/clipboard;1", "nsIClipboard");
  15.  
  16. const PrefService = Cc["@mozilla.org/preferences-service;1"];
  17. const prefs = PrefService.getService(Ci.nsIPrefBranch2);
  18. const prefService = PrefService.getService(Ci.nsIPrefService);
  19.  
  20. const reDBG = /extensions\.([^\.]*)\.(DBG_.*)/;
  21. const reDBG_FBS = /DBG_FBS_(.*)/;
  22.  
  23. var EOF = "<br/>";
  24.  
  25. // Register locale file with strings for the Tracing Console window.
  26. Firebug.registerStringBundle("chrome://firebug/locale/firebug-tracing.properties");
  27.  
  28. //************************************************************************************************
  29. //  The controller for the prefDomain Model.
  30. //  getOptionsMenuItems to create View, onPrefChangeHandler for View update
  31. //  base for trace viewers like tracePanel and traceConsole
  32. //  binds  to the branch 'prefDomain' of prefs
  33.  
  34. Firebug.TraceOptionsController = function(prefDomain, onPrefChangeHandler)
  35. {
  36.     this.prefDomain = prefDomain;
  37.  
  38.     this.traceService = Cc["@joehewitt.com/firebug-trace-service;1"]
  39.         .getService(Ci.nsISupports).wrappedJSObject;
  40.  
  41.     this.addObserver = function()
  42.     {
  43.         prefs.setBoolPref("browser.dom.window.dump.enabled", true);
  44.         this.observer = { observe: bind(this.observe, this) };
  45.         prefs.addObserver(prefDomain, this.observer, false);
  46.     };
  47.  
  48.     this.removeObserver = function()
  49.     {
  50.         prefs.removeObserver( prefDomain, this.observer, false);
  51.     };
  52.  
  53.     // nsIObserver
  54.     this.observe = function(subject, topic, data)
  55.     {
  56.         if (topic == "nsPref:changed")
  57.         {
  58.             var m = reDBG.exec(data);
  59.             if (m)
  60.             {
  61.                 var changedPrefDomain = "extensions." + m[1];
  62.                 if (changedPrefDomain == prefDomain)
  63.                 {
  64.                     var optionName = data.substr(prefDomain.length+1); // skip dot
  65.                     var optionValue = Firebug.getPref(prefDomain, m[2]);
  66.                     if (this.prefEventToUserEvent)
  67.                         this.prefEventToUserEvent(optionName, optionValue);
  68.                 }
  69.             }
  70.             else
  71.             {
  72.             }
  73.         }
  74.     };
  75.  
  76. //*************** UI ***************************************************************
  77.  
  78.     this.getOptionsMenuItems = function()  // Firebug menu items from option map
  79.     {
  80.         var optionMap = this.traceService.getTracer(prefDomain);
  81.         var items = [];
  82.         for (var p in optionMap)
  83.         {
  84.             var m = p.indexOf("DBG_");
  85.             if (m != 0)
  86.                 continue;
  87.  
  88.             try
  89.             {
  90.                 var prefValue = Firebug.getPref(this.prefDomain, p);
  91.                 var label = p.substr(4);
  92.                 items.push({
  93.                  label: label,
  94.                  nol10n: true,
  95.                  type: "checkbox",
  96.                  checked: prefValue,
  97.                  pref: p,
  98.                  command: bind(this.userEventToPrefEvent, this)
  99.                 });
  100.             }
  101.             catch (err)
  102.             {
  103.             }
  104.         }
  105.  
  106.         items.sort(function(a, b) {
  107.             return a.label > b.label;
  108.         });
  109.  
  110.         return items;
  111.     };
  112.  
  113.     this.userEventToPrefEvent = function(event)  // use as an event listener on UI control
  114.     {
  115.         var menuitem = event.target.wrappedJSObject;
  116.         if (!menuitem)
  117.             menuitem = event.target;
  118.  
  119.         var label = menuitem.getAttribute("label");
  120.         var category = 'DBG_'+label;
  121.         var value = Firebug.getPref(this.prefDomain, category);
  122.         var newValue = !value;
  123.  
  124.         Firebug.setPref(this.prefDomain, category, newValue);
  125.         prefService.savePrefFile(null);
  126.  
  127.     };
  128.  
  129.     if (onPrefChangeHandler)
  130.         this.prefEventToUserEvent = onPrefChangeHandler;
  131.     else
  132.     {
  133.         this.prefEventToUserEvent = function(optionName, optionValue)
  134.         {
  135.             FBTrace.sysout("TraceOptionsController owner needs to implement prefEventToUser Event", {name: optionName, value: optionValue});
  136.         };
  137.     }
  138.  
  139.     this.clearOptions = function()
  140.     {
  141.         var optionMap = this.traceService.getTracer(prefDomain);
  142.         var items = [];
  143.         for (var p in optionMap)
  144.         {
  145.             var m = p.indexOf("DBG_");
  146.             if (m != 0)
  147.                 continue;
  148.  
  149.             Firebug.setPref(this.prefDomain, p, false);
  150.         }
  151.         prefService.savePrefFile(null);
  152.     };
  153.  
  154. };
  155.  
  156. // ***********************************************************************************
  157. // Trace Module
  158.  
  159. Firebug.TraceModule = extend(Firebug.Module,
  160. {
  161.     dispatchName: "traceModule",
  162.  
  163.     initialize: function(prefDomain, prefNames)  // prefDomain is the calling app, firebug or chromebug
  164.     {
  165.         Firebug.Module.initialize.apply(this, arguments);
  166.  
  167.         FBTrace.DBG_OPTIONS = Firebug.getPref(prefDomain, "DBG_OPTIONS");
  168.  
  169.         this.prefDomain = prefDomain;
  170.  
  171.         // Open console automatically if the pref says so.
  172.         if (Firebug.getPref(this.prefDomain, "alwaysOpenTraceConsole"))
  173.             this.openConsole();
  174.  
  175.     },
  176.  
  177.     internationalizeUI: function(doc)
  178.     {
  179.         var elements = ["FirebugMenu_Options_alwaysOpenTraceConsole", "menu_openTraceConsole"];
  180.         for (var i=0; i<elements.length; i++)
  181.         {
  182.             var element = doc.getElementById(elements[i]);
  183.             if (element)
  184.                 FBL.internationalize(element, "label");
  185.         }
  186.     },
  187.  
  188.     shutdown: function()
  189.     {
  190.         if (this.consoleWindow && this.consoleWindow.TraceConsole)
  191.             this.consoleWindow.TraceConsole.unregisterModule(this);
  192.     },
  193.  
  194.     reattachContext: function(browser, context)
  195.     {
  196.     },
  197.  
  198.     openConsole: function(prefDomain, windowURL)
  199.     {
  200.         if (!prefDomain)
  201.             prefDomain = this.prefDomain;
  202.  
  203.         var self = this;
  204.         iterateBrowserWindows("FBTraceConsole", function(win) {
  205.             if (win.TraceConsole.prefDomain == prefDomain) {
  206.                 self.consoleWindow = win;
  207.                 return true;
  208.             }
  209.         });
  210.  
  211.         // Try to connect an existing trace-console window first.
  212.         if (this.consoleWindow && this.consoleWindow.TraceConsole) {
  213.             this.consoleWindow.TraceConsole.registerModule(this);
  214.             this.consoleWindow.focus();
  215.             return;
  216.         }
  217.  
  218.         if (!windowURL)
  219.             windowURL = "chrome://firebug/content/traceConsole.xul";
  220.  
  221.         var self = this;
  222.         var args = {
  223.             FBL: FBL,
  224.             Firebug: Firebug,
  225.             traceModule: self,
  226.             prefDomain: prefDomain,
  227.         };
  228.  
  229.         this.consoleWindow = window.openDialog(
  230.             windowURL,
  231.             "FBTraceConsole." + prefDomain,
  232.             "chrome,resizable,scrollbars=auto,minimizable,dialog=no",
  233.             args);
  234.     },
  235.  
  236.     // Trace console listeners
  237.     onLoadConsole: function(win, rootNode)
  238.     {
  239.         dispatch(this.fbListeners, "onLoadConsole", [win, rootNode]);
  240.     },
  241.  
  242.     onUnloadConsole: function(win)
  243.     {
  244.         dispatch(this.fbListeners, "onUnloadConsole", [win]);
  245.     },
  246.  
  247.     onDump: function(message)
  248.     {
  249.         dispatch(this.fbListeners, "onDump", [message]);
  250.     },
  251.  
  252.     dump: function(message, outputNodes)
  253.     {
  254.         // xxxHonza: find better solution for checking an ERROR messages
  255.         // (setup some rules).
  256.         var text = message.text;
  257.         if (text && (text.indexOf("ERROR") != -1 ||
  258.             text.indexOf("EXCEPTION") != -1 ||
  259.             text.indexOf("FAILS") != -1))
  260.         {
  261.             message.type = "DBG_ERRORS";
  262.         }
  263.  
  264.         Firebug.TraceModule.MessageTemplate.dump(message, outputNodes);
  265.     },
  266. });
  267.  
  268. Firebug.TraceModule.CommonBaseUI = {
  269.  
  270.     destroy: function()
  271.     {
  272.         this.optionsController.removeObserver();
  273.     },
  274.  
  275.     initializeContent: function(parentNode, outputNodes, prefDomain, callback)
  276.     {
  277.         var doc = parentNode.ownerDocument;
  278.  
  279.         // Create basic layout for trace console content.
  280.         var rep = Firebug.TraceModule.PanelTemplate;
  281.         rep.tag.replace({}, parentNode, rep);
  282.  
  283.         // This IFRAME is the container for all logs.
  284.         var logTabIframe = parentNode.getElementsByClassName("traceInfoLogsFrame").item(0);
  285.         var self = this;
  286.         logTabIframe.addEventListener("load", function(event)
  287.         {
  288.             var frameDoc = logTabIframe.contentWindow.document;
  289.  
  290.             addStyleSheet(frameDoc, createStyleSheet(frameDoc, "chrome://firebug/skin/panelbase.css"));
  291.             addStyleSheet(frameDoc, createStyleSheet(frameDoc, "chrome://firebug/skin/traceConsole.css"));
  292.  
  293.             var rootNode = frameDoc.getElementById("traceLogContent");
  294.             outputNodes.setScrollingNode(rootNode);
  295.  
  296.             var logNode = Firebug.TraceModule.MessageTemplate.createTable(rootNode);
  297.  
  298.             function recalcLayout() {
  299.                logTabIframe.style.height = (doc.defaultView.innerHeight - 25) + "px";
  300.             }
  301.  
  302.             doc.defaultView.addEventListener("resize", function(event) {
  303.                recalcLayout();
  304.             }, true);
  305.  
  306.             recalcLayout();
  307.  
  308.             callback(logNode);
  309.         }, true);
  310.  
  311.         // Initialize content for Options tab (a button for each DBG_ option).
  312.         var optionsBody = parentNode.getElementsByClassName("traceInfoOptionsText").item(0);
  313.         this.optionsController = new Firebug.TraceOptionsController(prefDomain, function updateButton(optionName, optionValue)
  314.         {
  315.             var button = parentNode.ownerDocument.getElementById(optionName);
  316.             if (button)
  317.                 button.setAttribute("checked", optionValue?"true":"false");
  318.             else
  319.                 FBTrace.sysout("traceModule onPrefChange no button with name "+optionName+ " in parentNode", parentNode);
  320.         });
  321.  
  322.         var menuitems = this.optionsController.getOptionsMenuItems();
  323.         for (var i=0; i<menuitems.length; i++)
  324.         {
  325.             var menuitem = menuitems[i];
  326.             var button = doc.createElement("button");
  327.             FBL.setClass(button, "traceOption");
  328.             FBL.setItemIntoElement(button, menuitem);
  329.             button.innerHTML = menuitem.label;
  330.             button.setAttribute("id", menuitem.pref);
  331.             button.removeAttribute("type");
  332.             button.addEventListener("click", menuitem.command, false);
  333.             optionsBody.appendChild(button);
  334.         }
  335.  
  336.         // Select default tab.
  337.         rep.selectTabByName(parentNode, "Logs");
  338.  
  339.         this.optionsController.addObserver();
  340.     },
  341.  
  342. };
  343.  
  344.  
  345. //************************************************************************************************
  346. //Trace Console Rep
  347.  
  348. Firebug.TraceModule.PanelTemplate = domplate({
  349.  
  350.     tag:
  351.         TABLE({class: "traceTable", cellpadding: 0, cellspacing: 0},
  352.             TBODY(
  353.                 TR({class: "traceInfoRow"},
  354.                     TD({class: "traceInfoCol"},
  355.                         DIV({class: "traceInfoBody"},
  356.                             DIV({class: "traceInfoTabs"},
  357.                                 A({class: "traceInfoLogsTab traceInfoTab", onclick: "$onClickTab",
  358.                                     view: "Logs"},
  359.                                     $STR("Logs")
  360.                                 ),
  361.                                 A({class: "traceInfoOptionsTab traceInfoTab", onclick: "$onClickTab",
  362.                                     view: "Options"},
  363.                                     $STR("Options")
  364.                                 )
  365.                             ),
  366.                             DIV({class: "traceInfoLogsText traceInfoText"},
  367.                                 IFRAME({class: "traceInfoLogsFrame",
  368.                                     src: "chrome://firebug/content/traceLogFrame.html"}
  369.                                 )
  370.                             ),
  371.                             DIV({class: "traceInfoOptionsText traceInfoText"})
  372.                         )
  373.                     )
  374.                 )
  375.             )
  376.         ),
  377.  
  378.     onClickTab: function(event)
  379.     {
  380.         this.selectTab(event.currentTarget);
  381.     },
  382.  
  383.     selectTabByName: function(parentNode, tabName)
  384.     {
  385.         var tab = parentNode.getElementsByClassName("traceInfo" + tabName + "Tab").item(0);
  386.         if (tab)
  387.             this.selectTab(tab);
  388.     },
  389.  
  390.     selectTab: function(tab)
  391.     {
  392.         var messageInfoBody = tab.parentNode.parentNode;
  393.  
  394.         var view = tab.getAttribute("view");
  395.         if (messageInfoBody.selectedTab)
  396.         {
  397.             messageInfoBody.selectedTab.removeAttribute("selected");
  398.             messageInfoBody.selectedText.removeAttribute("selected");
  399.         }
  400.  
  401.         var textBodyName = "traceInfo" + view + "Text";
  402.  
  403.         messageInfoBody.selectedTab = tab;
  404.         messageInfoBody.selectedText = getChildByClass(messageInfoBody, textBodyName);
  405.  
  406.         messageInfoBody.selectedTab.setAttribute("selected", "true");
  407.         messageInfoBody.selectedText.setAttribute("selected", "true");
  408.     }
  409. });
  410.  
  411. // ************************************************************************************************
  412. // Trace message
  413.  
  414. Firebug.TraceModule.MessageTemplate = domplate(Firebug.Rep,
  415. {
  416.     inspectable: false,
  417.  
  418.     tableTag:
  419.         TABLE({class: "messageTable", cellpadding: 0, cellspacing: 0},
  420.             TBODY()
  421.         ),
  422.  
  423.     rowTag:
  424.         TR({class: "messageRow $message|getMessageType",
  425.             _repObject: "$message",
  426.             $exception: "$message|isException",
  427.             onclick: "$onClickRow"},
  428.             TD({class: "messageNameCol messageCol"},
  429.                 DIV({class: "messageNameLabel messageLabel"},
  430.                     "$message|getMessageIndex")
  431.             ),
  432.             TD({class: "messageTimeCol messageCol"},
  433.                 DIV({class: "messageTimeLabel messageLabel"},
  434.                     "$message|getMessageTime")
  435.             ),
  436.             TD({class: "messageBodyCol messageCol"},
  437.                 DIV({class: "messageLabel", title: "$message|getMessageTitle"},
  438.                     "$message|getMessageLabel")
  439.             )
  440.         ),
  441.  
  442.     separatorTag:
  443.         TR({class: "messageRow separatorRow", _repObject: "$message"},
  444.             TD({class: "messageCol", colspan: "3"},
  445.                 DIV("$message|getMessageIndex")
  446.             )
  447.         ),
  448.  
  449.     bodyRow:
  450.         TR({class: "messageInfoRow"},
  451.             TD({class: "messageInfoCol", colspan: 8})
  452.         ),
  453.  
  454.     bodyTag:
  455.         DIV({class: "messageInfoBody", _repObject: "$message"},
  456.             DIV({class: "messageInfoTabs"},
  457.                 A({class: "messageInfoStackTab messageInfoTab", onclick: "$onClickTab",
  458.                     view: "Stack"},
  459.                     $STR("tracing.tab.Stack")
  460.                 ),
  461.                 A({class: "messageInfoExcTab messageInfoTab", onclick: "$onClickTab",
  462.                     view: "Exc",
  463.                     $collapsed: "$message|hideException"},
  464.                     $STR("tracing.tab.Exception")
  465.                 ),
  466.                 A({class: "messageInfoPropsTab messageInfoTab", onclick: "$onClickTab",
  467.                     view: "Props",
  468.                     $collapsed: "$message|hideProperties"},
  469.                     $STR("tracing.tab.Properties")
  470.                 ),
  471.                 A({class: "messageInfoScopeTab messageInfoTab", onclick: "$onClickTab",
  472.                     view: "Scope",
  473.                     $collapsed: "$message|hideScope"},
  474.                     $STR("tracing.tab.Scope")
  475.                 ),
  476.                 A({class: "messageInfoResponseTab messageInfoTab", onclick: "$onClickTab",
  477.                     view: "Response",
  478.                     $collapsed: "$message|hideResponse"},
  479.                     $STR("tracing.tab.Response")
  480.                 ),
  481.                 A({class: "messageInfoSourceTab messageInfoTab", onclick: "$onClickTab",
  482.                     view: "Source",
  483.                     $collapsed: "$message|hideSource"},
  484.                     $STR("tracing.tab.Source")
  485.                 ),
  486.                 A({class: "messageInfoIfacesTab messageInfoTab", onclick: "$onClickTab",
  487.                     view: "Ifaces",
  488.                     $collapsed: "$message|hideInterfaces"},
  489.                     $STR("tracing.tab.Interfaces")
  490.                 ),
  491.                 // xxxHonza: this doesn't seem to be much useful.
  492.                 /*A({class: "messageInfoTypesTab messageInfoTab", onclick: "$onClickTab",
  493.                     view: "Types",
  494.                     $collapsed: "$message|hideTypes"},
  495.                     "Types"
  496.                 ),*/
  497.                 A({class: "messageInfoObjectTab messageInfoTab", onclick: "$onClickTab",
  498.                     view: "Types",
  499.                     $collapsed: "$message|hideObject"},
  500.                     $STR("tracing.tab.Object")
  501.                 ),
  502.                 A({class: "messageInfoEventTab messageInfoTab", onclick: "$onClickTab",
  503.                     view: "Event",
  504.                     $collapsed: "$message|hideEvent"},
  505.                     $STR("tracing.tab.Event")
  506.                 )
  507.             ),
  508.             DIV({class: "messageInfoStackText messageInfoText"},
  509.                 TABLE({class: "messageInfoStackTable", cellpadding: 0, cellspacing: 0},
  510.                     TBODY(
  511.                         FOR("stack", "$message|stackIterator",
  512.                             TR(
  513.                                 TD({class: "stackFrame"},
  514.                                     A({class: "stackFrameLink", onclick: "$onClickStackFrame",
  515.                                         lineNumber: "$stack.lineNumber"},
  516.                                         "$stack.fileName"),
  517.                                     SPAN(" "),
  518.                                     SPAN("(", "$stack.lineNumber", ")"),
  519.                                     SPAN(" "),
  520.                                     SPAN({class: "stackFuncName"},
  521.                                         "$stack.funcName"),
  522.                                     A({class: "openDebugger", onclick: "$onOpenDebugger",
  523.                                         lineNumber: "$stack.lineNumber",
  524.                                         fileName: "$stack.fileName"},
  525.                                         "[...]")
  526.                                 )
  527.                             )
  528.                         )
  529.                     )
  530.                 )
  531.             ),
  532.             DIV({class: "messageInfoExcText messageInfoText"}),
  533.             DIV({class: "messageInfoPropsText messageInfoText"}),
  534.             DIV({class: "messageInfoResponseText messageInfoText"},
  535.                 IFRAME({class: "messageInfoResponseFrame"})
  536.             ),
  537.             DIV({class: "messageInfoSourceText messageInfoText"}),
  538.             DIV({class: "messageInfoIfacesText messageInfoText"}),
  539.             DIV({class: "messageInfoScopeText messageInfoText"}),
  540.             DIV({class: "messageInfoTypesText messageInfoText"}),
  541.             DIV({class: "messageInfoObjectText messageInfoText"}),
  542.             DIV({class: "messageInfoEventText messageInfoText"})
  543.         ),
  544.  
  545.     // Data providers
  546.     getMessageType: function(message)
  547.     {
  548.         return message.getType();
  549.     },
  550.  
  551.     getMessageIndex: function(message)
  552.     {
  553.         return message.index + 1;
  554.     },
  555.  
  556.     getMessageTime: function(message)
  557.     {
  558.         var date = new Date(message.time);
  559.         var m = date.getMinutes() + "";
  560.         var s = date.getSeconds() + "";
  561.         var ms = date.getMilliseconds() + "";
  562.         return "[" + ((m.length > 1) ? m : "0" + m) + ":" +
  563.             ((s.length > 1) ? s : "0" + s) + ":" +
  564.             ((ms.length > 2) ? ms : ((ms.length > 1) ? "0" + ms : "00" + ms)) + "]";
  565.     },
  566.  
  567.     getMessageLabel: function(message)
  568.     {
  569.         var maxLength = Firebug.getPref(Firebug.TraceModule.prefDomain,
  570.             "trace.maxMessageLength");
  571.         return message.getLabel(maxLength);
  572.     },
  573.  
  574.     getMessageTitle: function(message)
  575.     {
  576.         return message.getLabel(-1);
  577.     },
  578.  
  579.     isException: function(message)
  580.     {
  581.         return message.getException();
  582.     },
  583.  
  584.     hideProperties: function(message)
  585.     {
  586.         var props = message.getProperties();
  587.         for (var name in props)
  588.             return false;
  589.  
  590.         return true;
  591.     },
  592.  
  593.     hideScope: function(message)
  594.     {
  595.         return !message.getScope();
  596.     },
  597.  
  598.     hideInterfaces: function(message)
  599.     {
  600.         var ifaces = message.getInterfaces();
  601.         for (var name in ifaces)
  602.             return false;
  603.  
  604.         return true;
  605.     },
  606.  
  607.     hideTypes: function(message)
  608.     {
  609.         return !message.getTypes();
  610.     },
  611.  
  612.     hideObject: function(message)
  613.     {
  614.         return !message.getObject();
  615.     },
  616.  
  617.     hideEvent: function(message)
  618.     {
  619.         return !message.getEvent();
  620.     },
  621.  
  622.     hideException: function(message)
  623.     {
  624.         return !message.getException();
  625.     },
  626.  
  627.     hideResponse: function(message)
  628.     {
  629.         return !(message.obj instanceof Ci.nsIHttpChannel);
  630.     },
  631.  
  632.     hideSource: function(message)
  633.     {
  634.         return !(message.obj instanceof Ci.nsIHttpChannel);
  635.     },
  636.  
  637.     // Stack frame support
  638.     stackIterator: function(message)
  639.     {
  640.         return message.getStackArray();
  641.     },
  642.  
  643.     onClickStackFrame: function(event)
  644.     {
  645.         var winType = "FBTraceConsole-SourceView";
  646.         var lineNumber = event.target.getAttribute("lineNumber");
  647.  
  648.         openDialog("chrome://global/content/viewSource.xul",
  649.             winType, "all,dialog=no",
  650.             event.target.innerHTML, null, null, lineNumber, false);
  651.     },
  652.  
  653.     onOpenDebugger: function(event)
  654.     {
  655.         var target = event.target;
  656.         var lineNumber = target.getAttribute("lineNumber");
  657.         var fileName = target.getAttribute("fileName");
  658.  
  659.         if (typeof(ChromeBugOpener) == "undefined")
  660.             return;
  661.  
  662.         // Open Chromebug window.
  663.         var cbWindow = ChromeBugOpener.openNow();
  664.         FBTrace.sysout("Chromebug window has been opened", cbWindow);
  665.  
  666.         // xxxHonza: Open Chromebug with the source code file, scrolled automatically
  667.         // to the specified line number. Currently chrome bug doesn't return the window
  668.         // from ChromeBugOpener.openNow method. If it would be following code opens
  669.         // the source code file and scrolls to the given line.
  670.  
  671.         // Register onLoad listener and open the source file at the specified line.
  672.         if (cbWindow) {
  673.             cbWindow.addEventListener("load", function() {
  674.                 var context = cbWindow.FirebugContext;
  675.                 var link = new cbWindow.FBL.SourceLink(fileName, lineNumber, "js");
  676.                 Firebug.chrome.select(link, "script");
  677.             }, true);
  678.         }
  679.     },
  680.  
  681.     // Firebug rep support
  682.     supportsObject: function(message, type)
  683.     {
  684.         return message instanceof Firebug.TraceModule.TraceMessage;
  685.     },
  686.  
  687.     browseObject: function(message, context)
  688.     {
  689.         return false;
  690.     },
  691.  
  692.     getRealObject: function(message, context)
  693.     {
  694.         return message;
  695.     },
  696.  
  697.     // Context menu
  698.     getContextMenuItems: function(message, target, context)
  699.     {
  700.         var items = [];
  701.  
  702.         if (getAncestorByClass(target, "messageRow"))
  703.         {
  704.             items.push({
  705.               label: $STR("Cut"),
  706.               nol10n: true,
  707.               command: bindFixed(this.onCutMessage, this, message)
  708.             });
  709.  
  710.             items.push({
  711.               label: $STR("Copy"),
  712.               nol10n: true,
  713.               command: bindFixed(this.onCopyMessage, this, message)
  714.             });
  715.  
  716.             items.push("-");
  717.  
  718.             items.push({
  719.               label: $STR("Remove"),
  720.               nol10n: true,
  721.               command: bindFixed(this.onRemoveMessage, this, message)
  722.             });
  723.         }
  724.  
  725.         if (getAncestorByClass(target, "messageInfoStackText"))
  726.         {
  727.             items.push({
  728.               label: $STR("Copy Stack"),
  729.               nol10n: true,
  730.               command: bindFixed(this.onCopyStack, this, message)
  731.             });
  732.         }
  733.  
  734.         if (getAncestorByClass(target, "messageInfoExcText"))
  735.         {
  736.             items.push({
  737.               label: $STR("Copy Exception"),
  738.               nol10n: true,
  739.               command: bindFixed(this.onCopyException, this, message)
  740.             });
  741.         }
  742.  
  743.         if (items.length > 0)
  744.             items.push("-");
  745.  
  746.         items.push(this.optionMenu($STR("tracing.Show Time"), "trace.showTime"));
  747.         items.push(this.optionMenu($STR("tracing.Show Scope Variables"), "trace.enableScope"));
  748.         items.push("-");
  749.  
  750.         items.push({
  751.           label: $STR("tracing.cmd.Expand All"),
  752.           nol10n: true,
  753.           command: bindFixed(this.onExpandAll, this, message)
  754.         });
  755.  
  756.         items.push({
  757.           label: $STR("tracing.cmd.Collapse All"),
  758.           nol10n: true,
  759.           command: bindFixed(this.onCollapseAll, this, message)
  760.         });
  761.  
  762.         return items;
  763.     },
  764.  
  765.     optionMenu: function(label, option)
  766.     {
  767.         var checked = Firebug.getPref(Firebug.TraceModule.prefDomain, option);
  768.         return {label: label, type: "checkbox", checked: checked, nol10n: true,
  769.             command: bindFixed(Firebug.setPref, Firebug, Firebug.TraceModule.prefDomain,
  770.                 option, !checked) };
  771.     },
  772.  
  773.     getTooltip: function(message)
  774.     {
  775.         return message.text;
  776.     },
  777.  
  778.     // Context menu commands
  779.     onCutMessage: function(message)
  780.     {
  781.         this.onCopyMessage(message);
  782.         this.onRemoveMessage(message);
  783.     },
  784.  
  785.     onCopyMessage: function(message)
  786.     {
  787.         copyToClipboard(message.text);
  788.     },
  789.  
  790.     onRemoveMessage: function(message)
  791.     {
  792.         var row = message.row;
  793.         var parentNode = row.parentNode;
  794.         this.toggleRow(row, false);
  795.         parentNode.removeChild(row);
  796.     },
  797.  
  798.     onCopyStack: function(message)
  799.     {
  800.         copyToClipboard(message.getStack());
  801.     },
  802.  
  803.     onCopyException: function(message)
  804.     {
  805.         copyToClipboard(message.getException());
  806.     },
  807.  
  808.     onExpandAll: function(message)
  809.     {
  810.         var table = getAncestorByClass(message.row, "messageTable");
  811.         var rows = cloneArray(table.firstChild.childNodes);
  812.         for (var i=0; i<rows.length; i++)
  813.             this.expandRow(rows[i]);
  814.     },
  815.  
  816.     onCollapseAll: function(message)
  817.     {
  818.         var table = getAncestorByClass(message.row, "messageTable");
  819.         var rows = cloneArray(table.firstChild.childNodes);
  820.         for (var i=0; i<rows.length; i++)
  821.             this.collapseRow(rows[i]);
  822.     },
  823.  
  824.     // Clipboard helpers
  825.     copyToClipboard: function(text)
  826.     {
  827.         if (!text)
  828.             return;
  829.  
  830.         // Initialize transfer data.
  831.         var trans = CCIN("@mozilla.org/widget/transferable;1", "nsITransferable");
  832.         var wrapper = CCIN("@mozilla.org/supports-string;1", "nsISupportsString");
  833.         wrapper.data = text;
  834.         trans.addDataFlavor("text/unicode");
  835.         trans.setTransferData("text/unicode", wrapper, text.length * 2);
  836.  
  837.         // Set the data into the global clipboard
  838.         clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
  839.     },
  840.  
  841.     // Implementation
  842.     createTable: function(parentNode)
  843.     {
  844.         return HelperDomplate.replace(this.tableTag, {}, parentNode, this);
  845.     },
  846.  
  847.     dump: function(message, outputNodes, index)
  848.     {
  849.         var scrollingNode = outputNodes.getScrollingNode();
  850.         var scrolledToBottom = isScrolledToBottom(scrollingNode);
  851.  
  852.         var targetNode = outputNodes.getTargetNode();
  853.         // Set message index
  854.         if (index)
  855.             message.index = index;
  856.         else
  857.             message.index = targetNode.childNodes.length;
  858.  
  859.         // Insert log into the console.
  860.         var row = HelperDomplate.insertRows(this.rowTag, {message: message},
  861.             targetNode, this)[0];
  862.  
  863.         message.row = row;
  864.  
  865.         // Only if the manifest uses useNativeWrappers=no.
  866.         // The row in embedded frame, which uses type="content-primary", from some
  867.         // reason, this conten type changes wrapper around the row, so let's set
  868.         // directly thte wrappedJSObject here, so row-expand works.
  869.         if (row.wrappedJSObject)
  870.             row.wrappedJSObject.repObject = message;
  871.  
  872.         if (scrolledToBottom)
  873.             scrollToBottom(scrollingNode);
  874.     },
  875.  
  876.     dumpSeparator: function(outputNodes)
  877.     {
  878.         var panelNode = outputNodes.getScrollingNode();
  879.         var scrolledToBottom = isScrolledToBottom(panelNode);
  880.  
  881.         var targetNode = outputNodes.getTargetNode();
  882.         var fakeMessage = {};
  883.         fakeMessage.index = targetNode.childNodes.length;
  884.  
  885.         var row = HelperDomplate.insertRows(this.separatorTag, {message: fakeMessage},
  886.             targetNode, this)[0];
  887.  
  888.         if (scrolledToBottom)
  889.             scrollToBottom(panelNode);
  890.  
  891.         panelNode.scrollTop = panelNode.scrollHeight - panelNode.offsetHeight + 50;
  892.     },
  893.  
  894.     // Body of the message.
  895.     onClickRow: function(event)
  896.     {
  897.         if (isLeftClick(event))
  898.         {
  899.             var row = getAncestorByClass(event.target, "messageRow");
  900.             if (row)
  901.             {
  902.                 this.toggleRow(row);
  903.                 cancelEvent(event);
  904.             }
  905.         }
  906.     },
  907.  
  908.     collapseRow: function(row)
  909.     {
  910.         if (hasClass(row, "messageRow") && hasClass(row, "opened"))
  911.             this.toggleRow(row);
  912.     },
  913.  
  914.     expandRow: function(row)
  915.     {
  916.         if (hasClass(row, "messageRow"))
  917.             this.toggleRow(row, true);
  918.     },
  919.  
  920.     toggleRow: function(row, state)
  921.     {
  922.         var opened = hasClass(row, "opened");
  923.         if ((state != null) && (opened == state))
  924.              return;
  925.  
  926.         toggleClass(row, "opened");
  927.  
  928.         if (hasClass(row, "opened"))
  929.         {
  930.             var message = row.repObject;
  931.             if (!message && row.wrappedJSObject)
  932.                 message = row.wrappedJSObject.repObject;
  933.  
  934.             var bodyRow = HelperDomplate.insertRows(this.bodyRow, {}, row)[0];
  935.             var messageInfo = HelperDomplate.replace(this.bodyTag,
  936.                 {message: message}, bodyRow.firstChild);
  937.             message.bodyRow = bodyRow;
  938.  
  939.             this.selectTabByName(messageInfo, "Stack");
  940.         }
  941.         else
  942.         {
  943.             row.parentNode.removeChild(row.nextSibling);
  944.         }
  945.     },
  946.  
  947.     selectTabByName: function(messageInfoBody, tabName)
  948.     {
  949.         var tab = getChildByClass(messageInfoBody, "messageInfoTabs",
  950.             "messageInfo" + tabName + "Tab");
  951.         if (tab)
  952.             this.selectTab(tab);
  953.     },
  954.  
  955.     onClickTab: function(event)
  956.     {
  957.         this.selectTab(event.currentTarget);
  958.     },
  959.  
  960.     selectTab: function(tab)
  961.     {
  962.         var messageInfoBody = tab.parentNode.parentNode;
  963.  
  964.         var view = tab.getAttribute("view");
  965.         if (messageInfoBody.selectedTab)
  966.         {
  967.             messageInfoBody.selectedTab.removeAttribute("selected");
  968.             messageInfoBody.selectedText.removeAttribute("selected");
  969.         }
  970.  
  971.         var textBodyName = "messageInfo" + view + "Text";
  972.  
  973.         messageInfoBody.selectedTab = tab;
  974.         messageInfoBody.selectedText = getChildByClass(messageInfoBody, textBodyName);
  975.  
  976.         messageInfoBody.selectedTab.setAttribute("selected", "true");
  977.         messageInfoBody.selectedText.setAttribute("selected", "true");
  978.  
  979.         var message = Firebug.getRepObject(messageInfoBody);
  980.  
  981.         // Make sure the original Domplate is *not* tracing for now.
  982.         var dumpDOM = FBTrace.DBG_DOM;
  983.         FBTrace.DBG_DOM = false;
  984.         this.updateInfo(messageInfoBody, view, message);
  985.         FBTrace.DBG_DOM = dumpDOM;
  986.     },
  987.  
  988.     updateInfo: function(messageInfoBody, view, message)
  989.     {
  990.         var tab = messageInfoBody.selectedTab;
  991.         if (hasClass(tab, "messageInfoStackTab"))
  992.         {
  993.             // The content is generated by domplate template.
  994.         }
  995.         else if (hasClass(tab, "messageInfoPropsTab"))
  996.         {
  997.             this.updateInfoImpl(messageInfoBody, view, message, message.getProperties,
  998.                 function (message, valueBox, text) {
  999.                     Firebug.TraceModule.Tree.tag.replace({object: message.props}, valueBox,
  1000.                         Firebug.TraceModule.Tree);
  1001.                 });
  1002.         }
  1003.         else if (hasClass(tab, "messageInfoScopeTab"))
  1004.         {
  1005.             this.updateInfoImpl(messageInfoBody, view, message, message.getScope,
  1006.                 function (message, valueBox, text) {
  1007.                     Firebug.TraceModule.PropertyTree.tag.replace({object: message.scope}, valueBox,
  1008.                         Firebug.TraceModule.PropertyTree);
  1009.                 });
  1010.         }
  1011.         else if (hasClass(tab, "messageInfoIfacesTab"))
  1012.         {
  1013.             this.updateInfoImpl(messageInfoBody, view, message, message.getInterfaces,
  1014.                 function (message, valueBox, text) {
  1015.                     Firebug.TraceModule.Tree.tag.replace({object: message.ifaces}, valueBox,
  1016.                         Firebug.TraceModule.Tree);
  1017.                 });
  1018.         }
  1019.         else if (hasClass(tab, "messageInfoTypesTab"))
  1020.         {
  1021.             this.updateInfoImpl(messageInfoBody, view, message, message.getTypes);
  1022.         }
  1023.         else if (hasClass(tab, "messageInfoEventTab"))
  1024.         {
  1025.             this.updateInfoImpl(messageInfoBody, view, message, message.getEvent);
  1026.         }
  1027.         else if (hasClass(tab, "messageInfoObjectTab"))
  1028.         {
  1029.             this.updateInfoImpl(messageInfoBody, view, message, message.getProperties,
  1030.                 function (message, valueBox, text) {
  1031.                     if (message.obj instanceof Element)
  1032.                         Firebug.HTMLPanel.CompleteElement.tag.replace({object: message.obj}, valueBox,
  1033.                             Firebug.HTMLPanel.CompleteElement);
  1034.                     else
  1035.                         Firebug.TraceModule.PropertyTree.tag.replace({object: message.obj}, valueBox,
  1036.                             Firebug.TraceModule.PropertyTree);
  1037.                 });
  1038.         }
  1039.         else if (hasClass(tab, "messageInfoExcTab"))
  1040.         {
  1041.             this.updateInfoImpl(messageInfoBody, view, message, message.getException);
  1042.         }
  1043.         else if (hasClass(tab, "messageInfoResponseTab"))
  1044.         {
  1045.             this.updateInfoImpl(messageInfoBody, view, message, message.getResponse,
  1046.                 function (message, valueBox, text) {
  1047.                     var iframe = getChildByClass(valueBox, "messageInfoResponseFrame");
  1048.                     iframe.contentWindow.document.body.innerHTML = text;
  1049.                 });
  1050.         }
  1051.         else if (hasClass(tab, "messageInfoSourceTab"))
  1052.         {
  1053.             this.updateInfoImpl(messageInfoBody, view, message, message.getResponse,
  1054.                 function (message, valueBox, text) {
  1055.                     if (text)
  1056.                         insertWrappedText(text, valueBox);
  1057.                 });
  1058.         }
  1059.     },
  1060.  
  1061.     updateInfoImpl: function(messageInfoBody, view, message, getter, setter)
  1062.     {
  1063.         var valueBox = getChildByClass(messageInfoBody, "messageInfo" + view + "Text");
  1064.         if (!valueBox.valuePresented)
  1065.         {
  1066.             var text = getter.apply(message);
  1067.             if (typeof(text) != "undefined")
  1068.             {
  1069.                 valueBox.valuePresented = true;
  1070.  
  1071.                 if (setter)
  1072.                     setter(message, valueBox, text);
  1073.                 else
  1074.                     valueBox.innerHTML = text;
  1075.             }
  1076.         }
  1077.     }
  1078. });
  1079.  
  1080. // ************************************************************************************************
  1081. // Helper Domplate object that doesn't trace.
  1082.  
  1083. var HelperDomplate = (function()
  1084. {
  1085.     // Private helper function.
  1086.     function execute()
  1087.     {
  1088.         var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
  1089.  
  1090.         // Make sure the original Domplate is *not* tracing for now.
  1091.         if (typeof FBTrace != "undefined") {
  1092.             var dumpDOM = FBTrace.DBG_DOM;
  1093.             FBTrace.DBG_DOM = false;
  1094.         }
  1095.  
  1096.         var retValue = fn.apply(object, args);
  1097.  
  1098.         if (typeof FBTrace != "undefined")
  1099.             FBTrace.DBG_DOM = dumpDOM;
  1100.  
  1101.         return retValue;
  1102.     }
  1103.  
  1104.     return {
  1105.         insertRows: function(tag, args, parentNode, self)
  1106.         {
  1107.             return execute(tag.insertRows, tag, args, parentNode, self);
  1108.         },
  1109.  
  1110.         replace: function(tag, args, parentNode, self)
  1111.         {
  1112.             return execute(tag.replace, tag, args, parentNode, self);
  1113.         }
  1114.    }
  1115. }());
  1116.  
  1117. // ************************************************************************************************
  1118. // Trace Message Object
  1119.  
  1120. Firebug.TraceModule.TraceMessage = function(type, text, obj, scope, time)
  1121. {
  1122.     this.type = type;
  1123.     this.text = text;
  1124.     this.obj = obj;
  1125.     this.stack = [];
  1126.     this.scope = scope;
  1127.     this.time = time;
  1128.  
  1129.     if (this.obj instanceof Ci.nsIScriptError)
  1130.     {
  1131.         var trace = Firebug.errorStackTrace;
  1132.         if (trace)
  1133.         {
  1134.             for (var i=0; i<trace.frames.length; i++)
  1135.             {
  1136.                 var frame = trace.frames[i];
  1137.                 if (frame.href && frame.line)
  1138.                     this.stack.push({fileName:frame.href, lineNumber:frame.line, funcName:""});
  1139.             }
  1140.         }
  1141.         else
  1142.         {
  1143.             // Put info about the script error location into the stack.
  1144.             this.stack.push({fileName:this.obj.sourceName, lineNumber:this.obj.lineNumber, funcName:""});
  1145.         }
  1146.     }
  1147.     //xxxHonza: the object doesn't have to always be an instance of Error.
  1148.     else if (this.obj && this.obj.stack && /*(this.obj instanceof Error) &&*/
  1149.         (typeof this.obj.stack.split == "function"))
  1150.     {
  1151.         // If the passed object is an error with stack trace attached, use it.
  1152.         // This stack trace points directly to the place where the error occurred.
  1153.         var stack = this.obj.stack.split("\n");
  1154.         for (var i=0; i<stack.length; i++)
  1155.         {
  1156.             var frame = stack[i].split("@");
  1157.             if (frame.length != 2)
  1158.                 continue;
  1159.  
  1160.             var index = frame[1].lastIndexOf(":");
  1161.             this.stack.push({
  1162.                 fileName: frame[1].substr(0, index),
  1163.                 lineNumber: frame[1].substr(index+1),
  1164.                 funcName: frame[0]
  1165.             });
  1166.         }
  1167.     }
  1168.     else
  1169.     {
  1170.         // Initialize stack trace info. This must be done now, when the stack
  1171.         // is available.
  1172.         for (var frame = Components.stack, i=0; frame; frame = frame.caller, i++)
  1173.         {
  1174.             // Skip frames related to the tracing code.
  1175.             var fileName = unescape(frame.filename ? frame.filename : "");
  1176.             var traceServiceFile = "firebug@software.joehewitt.com/components/firebug-trace-service.js";
  1177.             if (i < 6 || fileName.indexOf(traceServiceFile) != -1)
  1178.                 continue;
  1179.  
  1180.             var sourceLine = frame.sourceLine ? frame.sourceLine : "";
  1181.             var lineNumber = frame.lineNumber ? frame.lineNumber : "";
  1182.             this.stack.push({fileName:fileName, lineNumber:lineNumber, funcName:""});
  1183.         }
  1184.     }
  1185.  
  1186.     if (this.obj instanceof Ci.nsICachingChannel)
  1187.     {
  1188.         try
  1189.         {
  1190.             var cacheToken = this.obj.cacheToken;
  1191.             if (cacheToken instanceof Ci.nsICacheEntryDescriptor)
  1192.             {
  1193.                 this.cacheClient = cacheToken.clientID;
  1194.                 this.cacheKey = cacheToken.key;
  1195.             }
  1196.         }
  1197.         catch (e)
  1198.         {
  1199.         }
  1200.     }
  1201.  
  1202.     if (this.obj instanceof Error ||
  1203.         this.obj instanceof Ci.nsIException ||
  1204.         this.obj instanceof Ci.nsIScriptError)
  1205.     {
  1206.         // Put the error message into the title so, it's immediately visible.
  1207.         this.text += " " + this.obj.message;
  1208.     }
  1209.  
  1210.     // Get snapshot of all properties now, as they can be changed.
  1211.     this.getProperties();
  1212.  
  1213.     // Get current scope
  1214.     this.getScope();
  1215. }
  1216.  
  1217. // ************************************************************************************************
  1218.  
  1219. Firebug.TraceModule.TraceMessage.prototype =
  1220. {
  1221.     getType: function()
  1222.     {
  1223.         return this.type;
  1224.     },
  1225.  
  1226.     getLabel: function(maxLength)
  1227.     {
  1228.         if (!this.text)
  1229.             return "";
  1230.  
  1231.         if (maxLength <= 10 || this.text.length <= maxLength)
  1232.             return this.text.replace(/[\n]/g,"");
  1233.  
  1234.         return this.text.substr(0, maxLength - 3) + "...";
  1235.     },
  1236.  
  1237.     getStackArray: function()
  1238.     {
  1239.         return this.stack;
  1240.     },
  1241.  
  1242.     getStack: function()
  1243.     {
  1244.         var result = "";
  1245.         for (var i=0; i<this.stack.length; i++) {
  1246.             var frame = this.stack[i];
  1247.             result += frame.fileName + " (" + frame.lineNumber + ")\n";
  1248.         }
  1249.  
  1250.         return result;
  1251.     },
  1252.  
  1253.     getProperties: function()
  1254.     {
  1255.         if (this.props)
  1256.             return this.props;
  1257.  
  1258.         this.props = [];
  1259.  
  1260.         if (this.obj instanceof Array)
  1261.         {
  1262.             if (this.obj.length)
  1263.             {
  1264.                 for (var p=0; p<this.obj.length; p++)
  1265.                 {
  1266.                     try
  1267.                     {
  1268.                         var getter = this.obj.__lookupGetter__(p);
  1269.                         if (getter)
  1270.                             this.props[p] = "" + getter;
  1271.                         else
  1272.                             this.props[p] = "" + this.obj[p];
  1273.                     }
  1274.                     catch (e)
  1275.                     {
  1276.                         onPanic("instanceof Array with length, item "+p, e);
  1277.                     }
  1278.                 }
  1279.             }
  1280.             else
  1281.             {
  1282.                 for (var p in this.obj)
  1283.                 {
  1284.                     try
  1285.                     {
  1286.                         var subProps = this.props[p] = [];
  1287.                         var subobj = this.obj.__lookupGetter__(p);
  1288.                         if (!subobj)
  1289.                             subobj = this.obj[p];
  1290.                         for (var p1 in subobj)
  1291.                         {
  1292.                             var getter = subobj.lookupGetter__(p1);
  1293.                             if (getter)
  1294.                                 subProps[p1] = "" + getter;
  1295.                             else
  1296.                                 subProps[p1] = "" + subobj[p1];
  1297.                         }
  1298.                     }
  1299.                     catch (e)
  1300.                     {
  1301.                         onPanic("instanceof Array, item "+p, e);
  1302.                     }
  1303.                 }
  1304.             }
  1305.         }
  1306.         else if (typeof(this.obj) == "string")
  1307.         {
  1308.             this.props = this.obj;
  1309.         }
  1310.         else if (this.obj instanceof Ci.jsdIValue)
  1311.         {
  1312.             var listValue = {value: null}, lengthValue = {value: 0};
  1313.             this.obj.getProperties(listValue, lengthValue);
  1314.             for (var i = 0; i < lengthValue.value; ++i)
  1315.             {
  1316.                 var prop = listValue.value[i];
  1317.                 try {
  1318.                     var name = unwrapIValue(prop.name);
  1319.                     this.props[name] = "" + unwrapIValue(prop.value);
  1320.                 } catch (e) {
  1321.                     onPanic("instanceof jsdIValue, i="+i, e);
  1322.                 }
  1323.             }
  1324.         }
  1325.         else if (this.obj instanceof Ci.nsISupportsCString)
  1326.         {
  1327.             this.props = this.obj.data;
  1328.         }
  1329.         else
  1330.         {
  1331.             try
  1332.             {
  1333.                 this.props = {};
  1334.                 var propsTotal = 0;
  1335.                 for (var p in this.obj)
  1336.                 {
  1337.                     propsTotal++;
  1338.  
  1339.                     try {
  1340.                         if (this.obj.__lookupGetter__)
  1341.                             var getter = this.obj.__lookupGetter__(p);
  1342.                         if (getter)
  1343.                             var value = "" + getter;
  1344.                         else
  1345.                             var value = safeToString(this.obj[p]);
  1346.                         this.props[p] = value;
  1347.                     }
  1348.                     catch (err) {
  1349.                         window.dump(">>>>>>>>>>>>>>>> traceModule.getProperties FAILS with "+err+"\n");
  1350.                         window.dump(">>>>>>>>>>>>>>>> traceModule.getProperties FAILS on object "+safeToString(this.obj)+"\n");
  1351.                         this.props[p] = "{Error}";
  1352.                     }
  1353.                 }
  1354.             }
  1355.             catch (exc)
  1356.             {
  1357.                 window.dump(">>>>>>>>>>>>>>>> traceModule.getProperties enumeration FAILS after "+propsTotal+ " with "+exc+"\n");
  1358.                 window.dump(">>>>>>>>>>>>>>>> traceModule.getProperties enumeration FAILS on object "+safeToString(this.obj)+"\n");
  1359.                 if (this.obj instanceof Window)
  1360.                     window.dump(">>>>>>>>>>>>>>>> traceModule.getProperties enumeration FAILS window closed:"+this.obj.closed+"\n");
  1361.             }
  1362.         }
  1363.  
  1364.         return this.props;
  1365.     },
  1366.  
  1367.     getInterfaces: function()
  1368.     {
  1369.         if (this.ifaces)
  1370.             return this.ifaces;
  1371.  
  1372.         this.ifaces = [];
  1373.         for (var iface in Ci) {
  1374.             if (this.obj instanceof Ci[iface]) {
  1375.                 var ifaceProps = this.ifaces[iface] = [];
  1376.                 for (p in Ci[iface])
  1377.                     ifaceProps[p] = this.obj[p];
  1378.             }
  1379.         }
  1380.         return this.ifaces;
  1381.     },
  1382.  
  1383.    getScope: function()
  1384.    {
  1385.        if (!Firebug.getPref(Firebug.prefDomain, "trace.enableScope"))
  1386.            return null;
  1387.  
  1388.        if (this.scope)
  1389.            return this.scope;
  1390.  
  1391.        var scope = {};
  1392.        Firebug.Debugger.halt(function(frame)
  1393.        {
  1394.            for (var i=0; i<4 && frame; i++)
  1395.                frame = frame.callingFrame;
  1396.  
  1397.            if (frame)
  1398.            {
  1399.                var listValue = {value: null}, lengthValue = {value: 0};
  1400.                frame.scope.getProperties(listValue, lengthValue);
  1401.  
  1402.                for (var i=lengthValue.value-1; i>=0; i--)
  1403.                {
  1404.                    var prop = listValue.value[i];
  1405.                    var name = unwrapIValue(prop.name);
  1406.                    var value = unwrapIValue(prop.value);
  1407.  
  1408.                    if ((typeof(value) != "function") && name && value)
  1409.                        scope[name.toString()] = value.toString();
  1410.                }
  1411.            }
  1412.        });
  1413.  
  1414.        return this.scope = scope;
  1415.    },
  1416.  
  1417.     getResponse: function()
  1418.     {
  1419.         var result = null;
  1420.         try
  1421.         {
  1422.             var self = this;
  1423.             TabWatcher.iterateContexts(function(context) {
  1424.                 var url = self.obj.originalURI.spec;
  1425.                 return context.sourceCache.loadText(url);
  1426.             });
  1427.         }
  1428.         catch (err)
  1429.         {
  1430.         }
  1431.  
  1432.         return result;
  1433.     },
  1434.  
  1435.     getException: function()
  1436.     {
  1437.         if (this.err)
  1438.             return this.err;
  1439.  
  1440.         this.err = "";
  1441.  
  1442.         if (this.obj && this.obj.message)
  1443.             return this.obj.message;
  1444.  
  1445.         // xxxJJB: this isn't needed, instanceof does QI. try {this.obj = this.obj.QueryInterface(Ci.nsIException);} catch (err){}
  1446.         if (!this.obj)
  1447.             return null;
  1448.  
  1449.         if (this.obj instanceof Error || this.obj instanceof Ci.nsIException)
  1450.         {
  1451.             try
  1452.             {
  1453.                 this.err += "<span class='ExceptionMessage'>" + this.obj.message + "</span>" + EOF;
  1454.                 this.err += this.obj.name + EOF;
  1455.                 this.err += this.obj.fileName + "(" + this.obj.lineNumber+ ")" + EOF;
  1456.             }
  1457.             catch (err)
  1458.             {
  1459.                 onPanic("instanceof Error or nsIExcpetion", e);
  1460.             }
  1461.         }
  1462.  
  1463.         return this.err;
  1464.     },
  1465.  
  1466.     getTypes: function()
  1467.     {
  1468.         if (this.types)
  1469.             return this.types;
  1470.  
  1471.         this.types = "";
  1472.  
  1473.         try {
  1474.             var obj = this.obj;
  1475.             while (obj)
  1476.             {
  1477.                 this.types += "typeof = " + typeof(obj) + EOF;
  1478.                 if (obj)
  1479.                     this.types += "    constructor = " + obj.constructor + EOF;
  1480.  
  1481.                 obj = obj.prototype;
  1482.             }
  1483.         }
  1484.         catch (e)
  1485.         {
  1486.             onPanic("getTypes "+this.types, e);
  1487.         }
  1488.  
  1489.         return this.types;
  1490.     },
  1491.  
  1492.     getEvent: function()
  1493.     {
  1494.         if (!(this.obj instanceof Event))
  1495.             return;
  1496.  
  1497.         if (this.eventInfo)
  1498.             return this.eventInfo;
  1499.  
  1500.         this.eventInfo = "";
  1501.  
  1502.         try
  1503.         {
  1504.             if (this.obj.eventPhase == this.obj.AT_TARGET)
  1505.                 this.eventInfo += " at target ";
  1506.             else if (this.obj.eventPhase == this.obj.BUBBLING_PHASE)
  1507.                 this.eventInfo += " bubbling phase ";
  1508.             else
  1509.                 this.eventInfo += " capturing phase ";
  1510.  
  1511.             if (this.obj.relatedTarget)
  1512.                 this.eventInfo += this.obj.relatedTarget.tagName + "->";
  1513.  
  1514.             if (this.obj.currentTarget)
  1515.             {
  1516.                 if (this.obj.currentTarget.tagName)
  1517.                     this.eventInfo += this.obj.currentTarget.tagName + "->";
  1518.                 else
  1519.                     this.eventInfo += this.obj.currentTarget.nodeName + "->";
  1520.             }
  1521.  
  1522.             this.eventInfo += this.obj.target.tagName;
  1523.         }
  1524.         catch (err)
  1525.         {
  1526.             onPanic("event", err);
  1527.         }
  1528.  
  1529.         return this.eventInfo;
  1530.     },
  1531.  
  1532.     getObject: function()
  1533.     {
  1534.         return this.obj;
  1535.     }
  1536. }
  1537.  
  1538. var lastPanic = null;
  1539. function onPanic(contextMessage, errorMessage)
  1540. {
  1541.     var appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"].getService(Components.interfaces.nsIAppShellService);
  1542.     var win = appShellService.hiddenDOMWindow;
  1543.     // XXXjjb I cannot get these tests to work.
  1544.     //if (win.lastPanic && (win.lastPanic == errorMessage))
  1545.         win.dump("traceModule: "+contextMessage +" panic attack "+errorMessage+"\n");
  1546.     //else
  1547.     //alert("Firebug traceModule panics: "+errorMessage);
  1548.  
  1549.     win.lastPanic = errorMessage;
  1550. }
  1551.  
  1552. // ************************************************************************************************
  1553. // Domplate helpers - Tree (domplate widget)
  1554.  
  1555. /**
  1556.  * This object is intended as a domplate widget for displaying hierarchical
  1557.  * structure (tree). Specific tree should be derived from this object and
  1558.  * getMembers method should be implemented.
  1559.  */
  1560. Firebug.TraceModule.Tree = domplate(Firebug.Rep,
  1561. {
  1562.     tag:
  1563.         TABLE({class: "domTable", cellpadding: 0, cellspacing: 0, onclick: "$onClick"},
  1564.             TBODY(
  1565.                 FOR("member", "$object|memberIterator",
  1566.                     TAG("$member|getRowTag", {member: "$member"}))
  1567.             )
  1568.         ),
  1569.  
  1570.     rowTag:
  1571.         TR({class: "memberRow $member.open $member.type\\Row", $hasChildren: "$member.hasChildren",
  1572.             _repObject: "$member", level: "$member.level"},
  1573.             TD({class: "memberLabelCell",
  1574.                 style: "padding-left: $member.indent\\px; width:1%; white-space: nowrap"},
  1575.                 DIV({class: "memberLabel $member.type\\Label"}, "$member.name")
  1576.             ),
  1577.             TD({class: "memberValueCell", style: "width: 100%;"},
  1578.                 TAG("$member.tag", {object: "$member.value"})
  1579.             )
  1580.         ),
  1581.  
  1582.     loop:
  1583.         FOR("member", "$members",
  1584.             TAG("$member|getRowTag", {member: "$member"})),
  1585.  
  1586.     memberIterator: function(object)
  1587.     {
  1588.         return this.getMembers(object);
  1589.     },
  1590.  
  1591.     getRowTag: function(member)
  1592.     {
  1593.         return this.rowTag;
  1594.     },
  1595.  
  1596.     onClick: function(event)
  1597.     {
  1598.         if (!isLeftClick(event))
  1599.             return;
  1600.  
  1601.         var row = getAncestorByClass(event.target, "memberRow");
  1602.         var label = getAncestorByClass(event.target, "memberLabel");
  1603.         if (label && hasClass(row, "hasChildren"))
  1604.             this.toggleRow(row);
  1605.     },
  1606.  
  1607.     toggleRow: function(row)
  1608.     {
  1609.         var level = parseInt(row.getAttribute("level"));
  1610.  
  1611.         if (hasClass(row, "opened"))
  1612.         {
  1613.             removeClass(row, "opened");
  1614.  
  1615.             var tbody = row.parentNode;
  1616.             for (var firstRow = row.nextSibling; firstRow; firstRow = row.nextSibling) {
  1617.                 if (parseInt(firstRow.getAttribute("level")) <= level)
  1618.                     break;
  1619.  
  1620.                 tbody.removeChild(firstRow);
  1621.             }
  1622.         }
  1623.         else
  1624.         {
  1625.             setClass(row, "opened");
  1626.  
  1627.             var repObject = row.repObject;
  1628.             if (repObject) {
  1629.                 var members = this.getMembers(repObject.value, level+1);
  1630.                 if (members)
  1631.                     this.loop.insertRows({members: members}, row);
  1632.             }
  1633.         }
  1634.     },
  1635.  
  1636.     getMembers: function(object, level)
  1637.     {
  1638.         if (!level)
  1639.             level = 0;
  1640.  
  1641.         if (typeof(object) == "string")
  1642.             return [this.createMember("", "", object, level)];
  1643.  
  1644.         var members = [];
  1645.         for (var p in object) {
  1646.             var member = this.createMember("", p, object[p], level);
  1647.             if (object[p] instanceof Array)
  1648.                 member.tag = FirebugReps.Nada.tag;
  1649.             members.push(member);
  1650.         }
  1651.         return members;
  1652.     },
  1653.  
  1654.     createMember: function(type, name, value, level)
  1655.     {
  1656.         var rep = Firebug.getRep(value);
  1657.         var tag = rep.shortTag ? rep.shortTag : rep.tag;
  1658.         var valueType = typeof(value);
  1659.  
  1660.         var hasChildren = hasProperties(value) && !(value instanceof ErrorCopy) &&
  1661.             (valueType == "function" || (valueType == "object" && value != null)
  1662.             || (valueType == "string" && value.length > Firebug.stringCropLength));
  1663.  
  1664.         return {
  1665.             name: name,
  1666.             value: value,
  1667.             type: type,
  1668.             rowClass: "memberRow-" + type,
  1669.             open: "",
  1670.             level: level,
  1671.             indent: level*16,
  1672.             hasChildren: hasChildren,
  1673.             tag: tag
  1674.         };
  1675.     }
  1676. });
  1677.  
  1678. // ************************************************************************************************
  1679.  
  1680. Firebug.TraceModule.PropertyTree = domplate(Firebug.TraceModule.Tree,
  1681. {
  1682.     getMembers: function(object, level)
  1683.     {
  1684.         if (!level)
  1685.             level = 0;
  1686.  
  1687.         try
  1688.         {
  1689.             var members = [];
  1690.             for (var p in object)
  1691.             {
  1692.                 try
  1693.                 {
  1694.                     members.push(this.createMember("dom", p, object[p], level));
  1695.                 }
  1696.                 catch (e)
  1697.                 {
  1698.                 }
  1699.             }
  1700.         }
  1701.         catch (err)
  1702.         {
  1703.             FBTrace.sysout("Exception", err);
  1704.         }
  1705.  
  1706.         return members;
  1707.     }
  1708. });
  1709.  
  1710. // ************************************************************************************************
  1711. // Registration
  1712.  
  1713. Firebug.registerModule(Firebug.TraceModule);
  1714. Firebug.registerRep(Firebug.TraceModule.MessageTemplate);
  1715.  
  1716. // ************************************************************************************************
  1717.  
  1718. }});
  1719.